package at.donalfonso.loadbalancer;

import java.util.ArrayList;
import java.util.Random;

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;

import at.donalfonso.model.Bestellung;
import at.donalfonso.model.LieferBestellung;
import at.donalfonso.model.LieferBestellungStatus;
import at.donalfonso.utility.JMSUtility;

public class LoadBalancerJMS extends Thread implements MessageListener{

	JMSUtility filiale1;
	JMSUtility filiale2;
	
	String id;
	
	ArrayList<Bestellung> filialQueue1 = new ArrayList<Bestellung>();
	ArrayList<Bestellung> filialQueue2 = new ArrayList<Bestellung>();

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		LoadBalancerJMS loadBalancer; 

		if (args.length < 1) {
			System.out.println("Usage: java LoadBalancer <ID>");
			loadBalancer = new LoadBalancerJMS("L" + new Random().nextInt(100));
		} 
		else {
			loadBalancer = new LoadBalancerJMS(args[0]);
		}

		//loadBalancer.run();
	}


	public LoadBalancerJMS(String id){
		this.id = id;
		try{
			filiale1 = new JMSUtility(1);
			filiale2 = new JMSUtility(2);

			filiale1.getConnection().start();
			filiale2.getConnection().start();

			Topic lieferTopic1 = filiale1.getTopicLieferung();
			Topic lieferTopic2 = filiale2.getTopicLieferung();

			filiale1.getSession().createConsumer(lieferTopic1).setMessageListener(this);
			filiale2.getSession().createConsumer(lieferTopic2).setMessageListener(this);

		}catch(Exception e){
			System.out.println("Error: "+e);
		}

	}

	public void balanceLoad(){

		
		int size1 = filialQueue1.size();
		int size2 = filialQueue2.size();

		System.out.println("Balance Load: "+size1+" - "+size2 );
		
		LieferBestellung bestellung;
		
		if (size1-size2 > 3){
			// Verschiebe von Filiale 1 nach Filiale2
			bestellung = readBestellung(filiale1);

			if (bestellung != null){
				bestellung.setLoadBalancer(id);
				writeBestellung(filiale2, bestellung);
				System.out.println("Von Filiale 1 nach Filiale 2 verschoben: "+bestellung.getBestellungId());
			}else{
				return;
			}
		}

		if (size2-size1 > 3){
			// Verschiebe von Filiale2 nach Filiale1
			bestellung = readBestellung(filiale2);
			if (bestellung != null){
				bestellung.setLoadBalancer(id);
				writeBestellung(filiale1, bestellung);
				System.out.println("Von Filiale 2 nach Filiale 1 verschoben: "+bestellung.getBestellungId());
			}
		}
	}

	public LieferBestellung readBestellung(JMSUtility filiale){
		LieferBestellung bestellung = null;
		
		try{
			Session session = filiale.getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);
			Queue queue = filiale.getQueueBestellungAufgenommen(); 
			String selector = "CLASS = 'lieferBestellung'";
			MessageConsumer consumer = session.createConsumer(queue, selector);
			Message message = consumer.receive(1000);
			consumer.close();
			session.close();
			
			if (message == null){
				return null;
			}
			
			bestellung = (LieferBestellung)((ObjectMessage) message).getObject();
			LieferBestellungStatus status = bestellung.getLieferstatus();
			bestellung.setLieferstatus(LieferBestellungStatus.VERSCHOBEN);
			
			session = filiale.getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);
			Topic topic = filiale.getTopicGuiUpdate();
			MessageProducer producer = session.createProducer(topic);
			message = session.createObjectMessage(bestellung);
			producer.send(message);
			producer.close();
			session.close();
			
			// Status zurücksetzen
			bestellung.setLieferstatus(status);
			
		}catch(Exception e){
			System.out.println(e);
		}
		return bestellung;
	}

	public void writeBestellung(JMSUtility filiale, Bestellung bestellung){
		try{
			Session session = filiale.getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);
			Queue queue = filiale.getQueueBestellungAufgenommen(); 
			MessageProducer producer = session.createProducer(queue);
			ObjectMessage message = session.createObjectMessage(bestellung);
			message.setStringProperty("STATUS", "NEU");
			producer.send(message);
			producer.close();
			
			// In das GUI Topic der Filiale schreiben und in das Topic Lieferung schreiben
			Topic topic = filiale.getTopicGuiUpdate();
			producer = session.createProducer(topic);
			producer.send(message);
			producer.close();
			
			topic = filiale.getTopicLieferung();
			producer = session.createProducer(topic);
			producer.send(message);
			producer.close();
			
			session.close();

		}catch(Exception e){
			System.out.println(e);
		}
	}

	@Override
	public void onMessage(Message arg0) {
		try {
			Destination quelle = arg0.getJMSDestination();
			String status = arg0.getStringProperty("STATUS");

			Bestellung bestellung = (Bestellung) ((ObjectMessage) arg0).getObject();

			// Für den Loadbalancer sind nur Lieferbestellungen relevant.
			if (!(bestellung instanceof LieferBestellung)) return;
			
			System.out.println("Message: "+quelle+" : "+bestellung.getBestellungId()+" : "+status);

			if (quelle.equals(filiale1.getTopicLieferung())){
				if ("NEU".equals(status)){
					filialQueue1.add(bestellung);
					System.out.println("Bestellung hinzugefügt: "+bestellung.getBestellungId()+" : "+filialQueue1.size());
				}else{
					System.out.println("entferne: "+filialQueue1.size());
					filialQueue1.remove(bestellung);
					System.out.println("entferne: "+filialQueue1.size());
					System.out.println("Bestellung entfernt: "+bestellung.getBestellungId()+" : "+filialQueue1.size());
				}
			}else{
				if ("NEU".equals(status)){
					filialQueue2.add(bestellung);
					System.out.println("Bestellung hinzugefügt: "+bestellung.getBestellungId()+" : "+filialQueue2.size());
				}else{
					filialQueue2.remove(bestellung);
					System.out.println("Bestellung entfernt: "+bestellung.getBestellungId()+" : "+filialQueue2.size());
				}
			}

			balanceLoad();

		} catch (JMSException e) {
			e.printStackTrace();
		}
	}

}
